const padNumber = number => {
  const value = typeof number === "number" ? number : parseInt(number, 10);
  if (Number.isNaN(value)) return "00";
  return value < 10 ? `0${value}` : `${value}`;
};

const callCloud = options => {
  const app = (typeof getApp === "function") ? getApp() : null;
  if (app && typeof app.callCloudFunction === "function") {
    return app.callCloudFunction(options);
  }

  const opts = options || {};
  const showLoading =
    Object.prototype.hasOwnProperty.call(opts, "showLoading")
      ? !!opts.showLoading
      : true;
  const { loadingTitle } = opts;
  const rest = Object.assign({}, opts);
  delete rest.loadingTitle;
  delete rest.showLoading;

  if (!showLoading) {
    return wx.cloud.callFunction(rest);
  }

  const title =
    typeof loadingTitle === "string" && loadingTitle.trim()
      ? loadingTitle.trim()
      : "\u52a0\u8f7d\u4e2d...";
  wx.showLoading({ title, mask: true });
  const originalComplete = rest.complete;
  rest.complete = (...args) => {
    wx.hideLoading();
    if (typeof originalComplete === "function") {
      originalComplete(...args);
    }
  };
  return wx.cloud.callFunction(rest);
};

const TAB_KEY_ALL = "all";
const DEFAULT_EMPTY_TEXT = "\u6682\u65e0\u6d3b\u52a8";

Page({
  data: {
    tabs: [
      { key: TAB_KEY_ALL, title: "\u5168\u90e8\u6d3b\u52a8" }
    ],
    activeTab: TAB_KEY_ALL,
    activities: [],
    loading: true,
    loadingMore: false,
    hasMore: true,
    page: 1,
    errorMessage: "",
    emptyText: DEFAULT_EMPTY_TEXT
  },

  onLoad() {
    this.activityImageCache = {};
    this.tabStates = {};
    this.ensureTabState(TAB_KEY_ALL);
    this.fetchActivities({ tabKey: TAB_KEY_ALL, reset: true });
  },

  onPullDownRefresh() {
    this.fetchActivities({
      tabKey: TAB_KEY_ALL,
      reset: true,
      done: () => wx.stopPullDownRefresh()
    });
  },

  onReachBottom() {
    const state = this.ensureTabState(TAB_KEY_ALL);
    if (state.loading || state.loadingMore || !state.hasMore) return;
    this.fetchActivities({ tabKey: TAB_KEY_ALL, reset: false });
  },

  handleActivityTap(event) {
    const dataset = event && event.currentTarget ? event.currentTarget.dataset : null;
    const activityId = dataset ? dataset.activityId : "";
    if (!activityId) return;
    wx.navigateTo({
      url: `/pages/activity-detail/activity-detail?activityID=${activityId}`
    });
  },

  handleTabChange(event) {
    const dataset = event && event.currentTarget ? event.currentTarget.dataset : {};
    const tabKey = dataset && dataset.tab ? dataset.tab : "";
    if (!tabKey || tabKey === this.data.activeTab) return;

    const state = this.ensureTabState(tabKey);
    this.setData({
      activeTab: tabKey,
      activities: state.activities,
      page: state.page || 1,
      hasMore: state.hasMore,
      loading: state.loading,
      loadingMore: state.loadingMore,
      errorMessage: state.errorMessage,
      emptyText: this.getEmptyText(tabKey)
    });

    if (!state.initialized && !state.loading) {
      this.fetchActivities({ tabKey, reset: true });
    }
  },

  fetchActivities({ reset = false, done, tabKey } = {}) {
    const key = tabKey || this.data.activeTab || TAB_KEY_ALL;
    const state = this.ensureTabState(key);
    const nextPage = reset ? 1 : state.page + 1;

    if (reset) {
      state.activities = [];
      state.page = 0;
      state.hasMore = true;
      state.loading = true;
      state.loadingMore = false;
      state.errorMessage = "";
    } else {
      state.loadingMore = true;
      state.errorMessage = "";
    }

    this.updateViewFromState(key);

    this.fetchActivityChunk("queryRegisteredActivities", nextPage, true)
      .then(ongoing =>
        this.fetchActivityChunk("queryFinishedRegisteredActivities", nextPage, false)
          .then(finished => [ongoing, finished])
      )
      .then(([ongoing, finished]) => {
        const combined = this.mergeActivities([ongoing.list, finished.list]);
        const hasMore =
          (Array.isArray(ongoing.list) && ongoing.list.length > 0) ||
          (Array.isArray(finished.list) && finished.list.length > 0);
        const hadSuccess = !ongoing.error || !finished.error;

        if (!hadSuccess && combined.length === 0) {
          const message =
            (ongoing.error && ongoing.error.message) ||
            (finished.error && finished.error.message) ||
            "\u6d3b\u52a8\u52a0\u8f7d\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5";
          throw new Error(message);
        }

        return this.resolveActivityImages(combined)
          .catch(() => combined)
          .then(resolved => ({ resolved, hasMore }));
      })
      .then(({ resolved, hasMore }) => {
        this.applyActivityListUpdate(resolved, nextPage, !!reset, key, hasMore);
      })
      .catch(error => {
        console.error("fetchActivities failed", error);
        state.errorMessage =
          (error && error.message) || "\u6d3b\u52a0\u52a0\u8f7d\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5";
        state.loading = false;
        state.loadingMore = false;
        this.updateViewFromState(key);
      })
      .finally(() => {
        if (typeof done === "function") done();
      });
  },

  fetchActivityChunk(functionName, tableNumber, showLoading) {
    return new Promise(resolve => {
      callCloud({
        name: functionName,
        data: { tableNumber },
        showLoading,
        loadingTitle: "\u52a0\u8f7d\u4e2d...",
        success: res => {
          const activities = this.normalizeActivities(res);
          resolve({ list: activities, error: null });
        },
        fail: err => {
          console.error(`${functionName} failed`, err);
          resolve({ list: [], error: err || new Error(`${functionName} failed`) });
        }
      });
    });
  },

  mergeActivities(lists) {
    const map = new Map();
    (lists || []).forEach(list => {
      (list || []).forEach(item => {
        if (!item || !item.activityID) return;
        const timestamp = this.getSortTimestamp(item);
        const existing = map.get(item.activityID);
        if (!existing || timestamp > existing.sortTimestamp) {
          map.set(item.activityID, { data: item, sortTimestamp: timestamp });
        }
      });
    });
    return Array.from(map.values())
      .sort((a, b) => b.sortTimestamp - a.sortTimestamp)
      .map(entry => entry.data);
  },

  getSortTimestamp(activity) {
    if (!activity || !Array.isArray(activity.activityTime)) {
      return 0;
    }
    let max = -Infinity;
    activity.activityTime.forEach(group => {
      if (!group || !group.startTime) return;
      const date = new Date(group.startTime);
      const value = date.getTime();
      if (Number.isFinite(value) && value > max) {
        max = value;
      }
    });
    return Number.isFinite(max) ? max : 0;
  },

  applyActivityListUpdate(list, nextPage, reset, tabKey, hasMoreOverride) {
    const key = tabKey || TAB_KEY_ALL;
    const state = this.ensureTabState(key);
    const mergedActivities = reset ? list : state.activities.concat(list);

    state.activities = mergedActivities;
    state.page = nextPage;
    state.hasMore =
      typeof hasMoreOverride === "boolean" ? hasMoreOverride : list.length > 0;
    state.loading = false;
    state.loadingMore = false;
    state.errorMessage = "";
    state.initialized = true;

    if (this.data.activeTab === key) {
      this.setData({
        activities: mergedActivities,
        page: nextPage,
        hasMore: state.hasMore,
        loading: false,
        loadingMore: false,
        errorMessage: "",
        emptyText: this.getEmptyText(key)
      });
    }
  },

  ensureTabState(tabKey) {
    if (!this.tabStates) {
      this.tabStates = {};
    }
    if (!this.tabStates[tabKey]) {
      this.tabStates[tabKey] = this.createInitialTabState();
    }
    return this.tabStates[tabKey];
  },

  createInitialTabState() {
    return {
      activities: [],
      loading: false,
      loadingMore: false,
      hasMore: true,
      page: 0,
      errorMessage: "",
      initialized: false
    };
  },

  updateViewFromState(tabKey) {
    if ((this.data && this.data.activeTab) !== tabKey) return;
    const state = this.ensureTabState(tabKey);
    this.setData({
      activities: state.activities,
      page: state.page || 1,
      hasMore: state.hasMore,
      loading: state.loading,
      loadingMore: state.loadingMore,
      errorMessage: state.errorMessage,
      emptyText: this.getEmptyText(tabKey)
    });
  },

  getEmptyText() {
    return DEFAULT_EMPTY_TEXT;
  },

  resolveActivityImages(activities) {
    if (!Array.isArray(activities) || activities.length === 0) {
      return Promise.resolve([]);
    }

    const cache = this.activityImageCache || (this.activityImageCache = {});
    const pending = [];

    activities.forEach(item => {
      const image = item && item.activityImage;
      if (typeof image === "string" && image.startsWith("cloud://") && !cache[image]) {
        pending.push(image);
      }
    });

    if (pending.length === 0) {
      return Promise.resolve(activities.map(activity => this.applyCachedImage(activity)));
    }

    const uniqueFileIDs = Array.from(new Set(pending));

    return wx.cloud
      .getTempFileURL({ fileList: uniqueFileIDs })
      .then(res => {
        const fileList = (res && res.fileList) || [];
        fileList.forEach(file => {
          if (file.status === 0 && file.tempFileURL) {
            cache[file.fileID] = file.tempFileURL;
          }
        });
        return activities.map(activity => this.applyCachedImage(activity));
      })
      .catch(err => {
        console.error("getTempFileURL failed", err);
        return activities;
      });
  },

  applyCachedImage(activity) {
    if (!activity) return activity;
    const cache = this.activityImageCache || {};
    const image = activity.activityImage;
    if (typeof image === "string" && cache[image]) {
      return Object.assign({}, activity, { activityImage: cache[image] });
    }
    return activity;
  },

  normalizeActivities(response) {
    const payload =
      (response && response.result) ? response.result : (response || {});
    const activities = Array.isArray(payload.activities)
      ? payload.activities
      : [];

    return activities.map(activity => {
      const safeActivity = activity || {};
      const startList = Array.isArray(safeActivity.activityStartTime)
        ? safeActivity.activityStartTime.map(startTime => ({ startTime }))
        : [];
      return {
        activityID: safeActivity.activityID || "",
        activityName: safeActivity.activityName || "\u672a\u547d\u540d\u6d3b\u52a8",
        activityImage: safeActivity.activityImage || "",
        activityTime: startList,
        activityLocation: safeActivity.activityLocation || "\u5730\u70b9\u5f85\u5b9a",
        activityTimeText: this.buildTimeText(startList)
      };
    });
  },

  buildTimeText(activityTime) {
    if (!Array.isArray(activityTime) || activityTime.length === 0) {
      return "\u65f6\u95f4\u5f85\u5b9a";
    }
    const parts = activityTime
      .map(group => this.formatGroupTime(group))
      .filter(text => text);
    return parts.length > 0 ? parts.join(" / ") : "\u65f6\u95f4\u5f85\u5b9a";
  },

  formatGroupTime(group) {
    if (!group) return "";
    const start = this.formatDateTime(group.startTime);
    return start || "";
  },

  formatDateTime(value) {
    if (!value) return "";
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) {
      return typeof value === "string" ? value : "";
    }
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const hours = padNumber(date.getHours());
    const minutes = padNumber(date.getMinutes());
    return `${month}\u6708${day}\u65e5 ${hours}:${minutes}`;
  }
});
